package org.cybzacg.convert.converters.numeric;

import org.cybzacg.convert.annotation.Converter;
import org.cybzacg.convert.context.ConversionContext;
import org.cybzacg.convert.core.AbstractTypeConverter;
import org.cybzacg.convert.enums.ConversionError;
import org.cybzacg.convert.enums.ConversionPriority;
import org.cybzacg.convert.enums.ConversionType;
import org.cybzacg.convert.exception.ConversionException;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * 数字类型转换器
 * 作为兜底转换器处理各种数字类型之间的转换
 * 当其他专门的数字转换器无法处理时，使用此转换器
 *
 * @author cybzacg.blog
 * @version 3.0
 * @since 3.0
 */
@Slf4j
@Converter(
    name = "NumberConverter",
    priority = ConversionPriority.HIGH,
    description = "数字类型转换器，处理各种数字类型之间的转换",
    enabled = true,
    version = "3.0",
    author = "cybzacg.blog",
    supportedTypes = {Number.class},
    tags = {"number", "numeric", "conversion"}
)
public class NumberConverter extends AbstractTypeConverter {

    public NumberConverter() {
        super(ConversionPriority.HIGH);
        log.debug("NumberConverter 初始化完成，优先级: {}", ConversionPriority.HIGH);
    }

    @Override
    protected ConversionType getConversionType() {
        return ConversionType.NUMBER;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected <T> T doConvert(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {

        log.debug("开始数字类型转换: 源类型={}, 目标类型={}, 默认值={}",
                 source != null ? source.getClass().getSimpleName() : "null",
                 targetType.getSimpleName(),
                 defaultValue);

        if (source == null) {
            log.debug("源对象为null，返回默认值: {}", defaultValue);
            return defaultValue;
        }

        // 如果已经是目标类型，直接返回
        if (targetType.isInstance(source)) {
            log.debug("源对象已经是目标类型，直接返回: {}", source);
            return (T) source;
        }

        try {
            // 转换为数值
            Number number = convertToNumber(source, context);
            if (number == null) {
                log.debug("转换为数值失败，返回默认值: {}", defaultValue);
                return defaultValue;
            }

            // 根据目标类型进行转换
            T result = convertNumberToTargetType(number, targetType, defaultValue, context);
            log.debug("数字类型转换完成: {} -> {}", source, result);
            return result;
        } catch (Exception e) {
            log.error("数字类型转换失败: {} -> {}", source, targetType, e);
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                "数字类型转换失败: " + source + " -> " + targetType, e);
        }
    }

    /**
     * 将对象转换为数值
     *
     * @param source 源对象
     * @param context 转换上下文
     * @return 数值对象
     * @throws ConversionException 转换失败时抛出
     */
    protected Number convertToNumber(Object source, ConversionContext context) throws ConversionException {
        if (source == null) {
            return null;
        }

        // 如果已经是Number类型，直接返回
        if (source instanceof Number number) { return number; }

        // 字符串转换
        if (source instanceof String) {
            String str = ((String) source).trim();
            if (str.isEmpty()) {
                return null;
            }

            try {
                // 尝试解析为整数
                if (str.matches("-?\\d+")) {
                    return Long.parseLong(str);
                }

                // 尝试解析为小数
                if (str.matches("-?\\d*\\.\\d+([eE][+-]?\\d+)?")) {
                    return Double.parseDouble(str);
                }

                // 尝试解析为BigDecimal
                return new BigDecimal(str);

            } catch (NumberFormatException e) {
                throw new ConversionException(ConversionError.FORMAT_ERROR,
                    "无法将字符串 '" + str + "' 转换为数值", e);
            }
        }

        // 布尔值转换
        if (source instanceof Boolean) {
            return ((Boolean) source) ? 1 : 0;
        }

        // 字符转换
        if (source instanceof Character) {
            return (int) (Character) source;
        }

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
            "无法将类型 " + source.getClass().getSimpleName() + " 转换为数值");
    }

    /**
     * 将数值转换为目标类型
     *
     * @param <T> 目标类型
     * @param number 源数值
     * @param targetType 目标类型
     * @param defaultValue 默认值
     * @param context 转换上下文
     * @return 转换结果
     * @throws ConversionException 转换失败时抛出
     */
    @SuppressWarnings("unchecked")
    protected <T> T convertNumberToTargetType(Number number, Class<T> targetType, T defaultValue, ConversionContext context)
            throws ConversionException {

        String typeName = targetType.getSimpleName();

        try {
            return switch (typeName) {
                case "byte", "Byte" -> (T) Byte.valueOf(number.byteValue());
                case "short", "Short" -> (T) Short.valueOf(number.shortValue());
                case "int", "Integer" -> (T) Integer.valueOf(number.intValue());
                case "long", "Long" -> (T) Long.valueOf(number.longValue());
                case "float", "Float" -> (T) Float.valueOf(number.floatValue());
                case "double", "Double" -> (T) Double.valueOf(number.doubleValue());
                case "BigDecimal" -> (T) convertToBigDecimal(number);
                case "BigInteger" -> (T) convertToBigInteger(number);
                case "Number" -> (T) number;
                default -> throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                    "不支持的数值类型: " + targetType);
            };
        } catch (ArithmeticException e) {
            throw new ConversionException(ConversionError.NUMBER_OVERFLOW,
                "数值溢出，无法将 " + number + " 转换为 " + targetType, e);
        } catch (Exception e) {
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                "数值转换失败: " + number + " -> " + targetType, e);
        }
    }

    /**
     * 转换为BigDecimal
     *
     * @param number 数值
     * @return BigDecimal
     */
    protected BigDecimal convertToBigDecimal(Number number) {
        if (number instanceof BigDecimal) {
            return (BigDecimal) number;
        } else if (number instanceof BigInteger) {
            return new BigDecimal((BigInteger) number);
        } else if (number instanceof Double || number instanceof Float) {
            return BigDecimal.valueOf(number.doubleValue());
        } else {
            return BigDecimal.valueOf(number.longValue());
        }
    }

    /**
     * 转换为BigInteger
     *
     * @param number 数值
     * @return BigInteger
     */
    protected BigInteger convertToBigInteger(Number number) {
        if (number instanceof BigInteger) {
            return (BigInteger) number;
        } else if (number instanceof BigDecimal) {
            return ((BigDecimal) number).toBigInteger();
        } else if (number instanceof Double || number instanceof Float) {
            return BigInteger.valueOf(number.longValue());
        } else {
            return BigInteger.valueOf(number.longValue());
        }
    }

    @Override
    public boolean supports(Class<?> targetType) {
        // 支持所有数字类型
        return Number.class.isAssignableFrom(targetType) ||
               targetType == byte.class || targetType == short.class ||
               targetType == int.class || targetType == long.class ||
               targetType == float.class || targetType == double.class ||
               targetType == Byte.class || targetType == Short.class ||
               targetType == Integer.class || targetType == Long.class ||
               targetType == Float.class || targetType == Double.class ||
               targetType == BigDecimal.class || targetType == BigInteger.class;
    }

    @Override
    public Class<?> getSupportedType() {
        return Number.class;
    }

    @Override
    public String getName() {
        return "NumberConverter";
    }

    /**
     * 安全的数值转换，避免精度丢失
     *
     * @param <T> 目标类型
     * @param value 源数值
     * @param targetType 目标类型
     * @return 转换结果
     * @throws ConversionException 转换失败时抛出
     */
    @SuppressWarnings("unchecked")
    public static <T> T convertSafely(Number value, Class<T> targetType) throws ConversionException {
        if (value == null) {
            return null;
        }

        String typeName = targetType.getSimpleName();

        try {
            return switch (typeName) {
                case "byte", "Byte" -> (T) Byte.valueOf(value.byteValue());
                case "short", "Short" -> (T) Short.valueOf(value.shortValue());
                case "int", "Integer" -> (T) Integer.valueOf(value.intValue());
                case "long", "Long" -> (T) Long.valueOf(value.longValue());
                case "float", "Float" -> (T) Float.valueOf(value.floatValue());
                case "double", "Double" -> (T) Double.valueOf(value.doubleValue());
                case "BigDecimal" -> (T) (value instanceof BigDecimal ? value : BigDecimal.valueOf(value.doubleValue()));
                case "BigInteger" -> (T) (value instanceof BigInteger ? value : BigInteger.valueOf(value.longValue()));
                default -> throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                    "不支持的数值类型: " + targetType);
            };
        } catch (Exception e) {
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                "安全数值转换失败: " + value + " -> " + targetType, e);
        }
    }

    /**
     * 检查数值类型
     *
     * @param clazz 类型
     * @return 是否为数值类型
     */
    public static boolean isNumberType(Class<?> clazz) {
        return Number.class.isAssignableFrom(clazz) ||
               clazz == byte.class || clazz == short.class || clazz == int.class ||
               clazz == long.class || clazz == float.class || clazz == double.class ||
               clazz == Byte.class || clazz == Short.class || clazz == Integer.class ||
               clazz == Long.class || clazz == Float.class || clazz == Double.class ||
               clazz == BigDecimal.class || clazz == BigInteger.class;
    }

    /**
     * 获取数值类型的默认值
     *
     * @param <T> 数值类型
     * @param clazz 类型
     * @return 默认值
     */
    @SuppressWarnings("unchecked")
    public static <T> T getDefaultValue(Class<T> clazz) {
        String typeName = clazz.getSimpleName();

        return switch (typeName) {
            case "byte", "Byte" -> (T) Byte.valueOf((byte) 0);
            case "short", "Short" -> (T) Short.valueOf((short) 0);
            case "int", "Integer" -> (T) Integer.valueOf(0);
            case "long", "Long" -> (T) Long.valueOf(0L);
            case "float", "Float" -> (T) Float.valueOf(0.0f);
            case "double", "Double" -> (T) Double.valueOf(0.0);
            case "BigDecimal" -> (T) BigDecimal.ZERO;
            case "BigInteger" -> (T) BigInteger.ZERO;
            default -> null;
        };
    }
}
